package com.fast.fast.common.mybatis.plus.aspect;

import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import com.fast.fast.common.base.exception.BizException;
import com.fast.fast.common.mybatis.plus.config.MyBatisPlusProperties;
import com.fast.fast.common.mybatis.plus.context.CustomContext;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Set;

/**
 * MybatisPlus自定义多租户切面
 *
 * @author lyf
 * @date 2022/01/01 00:00 周六
 * @date 2024/09/12 17:10 周四
 **/
@RequiredArgsConstructor
@Slf4j
@Aspect
public class CustomAspect {

    private final CustomContext context;

    private final MyBatisPlusProperties myBatisPlusProperties;

    /**
     * 定义切点(即被增强处理的点)
     */
    @Pointcut("@annotation(org.springframework.web.bind.annotation.PostMapping)")
    public void pointCut1() {
    }

    /**
     * 定义切点(即被增强处理的点)
     */
    @Pointcut("@annotation(org.springframework.web.bind.annotation.GetMapping)")
    public void pointCut2() {
    }

    /**
     * 定义切点(即被增强处理的点)
     */
    @Pointcut("@annotation(org.springframework.web.bind.annotation.PutMapping)")
    public void pointCut3() {
    }

    /**
     * 定义切点(即被增强处理的点)
     */
    @Pointcut("@annotation(org.springframework.web.bind.annotation.DeleteMapping)")
    public void pointCut4() {
    }

    /**
     * 定义切点(即被增强处理的点)
     */
    @Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping)")
    public void pointCut5() {
    }

    /**
     * 定义切点(即被增强处理的点)
     * 匹配任意返回值的com.fast包及其子孙包下的StpInterfaceImpl方法
     */
    @Pointcut("execution(* com.fast..*.StpInterfaceImpl.*(..))")
    public void pointCut6() {
    }

    /**
     * 环绕通知
     *
     * @param joinPoint 连接点
     * @return
     */
    @SneakyThrows
    @Around("pointCut1()||pointCut2()||pointCut3()||pointCut4()||pointCut5()||pointCut6()")
    public Object around(ProceedingJoinPoint joinPoint) {
        // 前置环绕通知
        //log.debug("前置环绕通知 around before");

        // 获取被注解的目标
        Object target = joinPoint.getTarget();
        // 获取被注解的类
        Class<?> aClass = target.getClass();
        // 获取被注解的类名
        String className = aClass.getName();
        // 获取被注解的方法名
        String methodName = joinPoint.getSignature().getName();
        // 打印类名、方法名
        //log.debug("类名:{},方法名:{}", className, methodName);

        // 放行的类不处理
        Set<String> excludeClassObjects = myBatisPlusProperties.getTenantAspect().getExcludeClasses();
        if (ObjUtil.isNotEmpty(excludeClassObjects) && excludeClassObjects.contains(className)) {
            return joinPoint.proceed();
        }

        // 放行的方法不处理
        Set<String> excludeMethods = myBatisPlusProperties.getTenantAspect().getExcludeMethods();
        if (ObjUtil.isNotEmpty(excludeMethods) && excludeMethods.contains(methodName)) {
            return joinPoint.proceed();
        }

        // 获取请求头中的租户id
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();
        String tenantId = request.getHeader("tenantId");
        if (StrUtil.isBlank(tenantId)) {
            throw new BizException("tenantId不能为空");
        }
        try {
            Long.parseLong(tenantId);
        } catch (Exception e) {
            throw new BizException("tenantId不能为空，当前tenantId为：" + tenantId);
        }
        // 注入到自定义多租户上下文中
        context.setCurrentTenantId(Long.parseLong(tenantId));

        // 获取方法入参
        //Object[] args = joinPoint.getArgs();
        //for (int i = 0; i < args.length; i++) {
        //    log.debug("目标方法入参{}:{}", i, args[i]);
        //}
        // 执行目标方法
        Object result = joinPoint.proceed();
        context.clear();
        // 获取方法出参
        //log.debug("目标方法出参:{}", result);

        // 后置环绕通知
        //log.debug("后置环绕通知 around after");
        return result;
    }

    /*
      切面通知顺序：
      前置环绕通知around before >> 前置通知before >> 执行目标方法proceed >> 后置通知afterReturning >> 最终通知after >> 后置环绕通知around after
      前置环绕通知around before >> 前置通知before >> 执行目标方法proceed >> 异常通知afterThrowing >> 最终通知after
     */

    ///**
    // * 前置通知
    // */
    //@Before("pointCut1()||pointCut2()||pointCut3()||pointCut4()||pointCut5()||pointCut6()")
    //public void before() {
    //    log.debug("前置通知 before,目标方法即将被执行");
    //}
    //
    ///**
    // * 后置通知
    // */
    //@AfterReturning("pointCut1()||pointCut2()||pointCut3()||pointCut4()||pointCut5()||pointCut6()")
    //public void afterReturning() {
    //    log.debug("后置通知 afterReturning，目标方法已执行完毕1");
    //}
    //
    ///**
    // * 最终通知
    // */
    //@After("pointCut1()||pointCut2()||pointCut3()||pointCut4()||pointCut5()||pointCut6()")
    //public void after() {
    //    log.debug("最终通知 after，目标方法已执行完毕2");
    //}
    //
    ///**
    // * 异常通知
    // */
    //@AfterThrowing("pointCut1()||pointCut2()||pointCut3()||pointCut4()||pointCut5()||pointCut6()")
    //public void afterThrowing() {
    //    log.debug("异常通知 afterThrowing，目标方法发生了异常");
    //}
}
